
/// <reference path="./Sets.ts" />
/// <reference path="./Queue.ts" />

namespace apeng {

	interface IEventDispatcherHandler {
		callBack: Function
		caller: any
		priority?: number
	}

	export interface IEventDispatcherEmit {
		eventName: string,
		target: IEventDispatcher
	}

	export interface IEventDispatcher {
		on(eventName: string | number, callBack: Function, caller: any): void,
		once?(eventName: string | number, callBack: Function, caller: any): void,
		off(eventName: string | number, callBack: Function, caller: any): void,
		emit?(eventName: string | number, ...param: any[]): void,
	}

	export const EventDispatcherEventType = {
		ON: "ON",
		OFF: "OFF",
		CLEAR: "CLEAR",
	}

	export class EventDispatcher implements IEventDispatcher {

		private eventMap: Map<string | number, IEventDispatcherHandler[]> = new Map()

		/**
		 * 注册事件
		 * @param eventName 
		 * @param callBack 派发队列事件 则( 按照优先级一个一个派发  第一个参数回调 complete 完成了后进行下一个)
		 * @param caller 
		 * @param priority 优先级 由小往大执行
		 */
		public on(eventName: string | number, callBack: Function, caller?: any, priority: number = 0) {
			if (typeof priority != "number")
				throw new Error("检查注册事件")

			let handler = this.eventMap.get(eventName)
			if (!handler) {
				handler = []
				this.eventMap.set(eventName, handler)
			}
			handler.push({ callBack, caller, priority })

			Sets.sortMin(handler, a => a.priority!)

			this.emit(EventDispatcherEventType.ON)

			return callBack
		}


		/**
		 * 如不返回 或返回为true则注销
		 * @param eventName 
		 * @param callBack 
		 * @param caller 
		 * @returns 
		 */
		public once(eventName: string | number, callBack: Function, caller?: any, priority: number = 0) {
			let _this = this
			let _callBack = function () {
				let isOff = callBack.apply(caller, arguments)
				if (isOff == undefined || isOff)
					_this.off(eventName, _callBack, caller)
			}
			this.on(eventName, _callBack, caller, priority)
			return _callBack
		}

		public off(eventName: string | number, callBack: Function, caller?: any) {
			let handler = this.eventMap.get(eventName)
			if (!handler)
				return
			for (let i = 0; i < handler.length; i++) {
				let event = handler[i]
				if (event && event.callBack == callBack && event.caller == caller) {
					handler.splice(i, 1)
					this.emit(EventDispatcherEventType.OFF)
					return
				}
			}
		}

		public offAll(eventName: string | number): boolean {
			return this.eventMap.delete(eventName)
		}

		public emit(eventName: string | number, ...param: any[]): any[] {
			let handler = this.eventMap.get(eventName)
			let res: any[] = []
			if (!handler)
				return res
			// 拷贝数组 以免中途删除
			handler = [...handler]
			for (let event of handler)
				res.push(event.callBack.apply(event.caller, param))

			return res
		}

		/**
		 * 派发队列事件
		 * @param eventName 
		 * @param complete 
		 * @param param 
		 * @returns 
		 */
		public emitQueue(eventName: string | number, interval: number = -1, complete?: () => void, log: string | ((index: number) => string) = "", ...param: any[]): Queue {
			let handler = this.eventMap.get(eventName)
			if (!handler) {
				if (complete)
					complete()
				return null!
			}
			// 拷贝数组 以免中途删除
			handler = [...handler]

			if (handler.length == 0) {
				if (complete)
					complete()
				return null!
			}

			let queue = Queue.pool.get()
			queue.interval = interval
			queue.log = log
			queue.complete = () => {
				if (complete)
					complete()
				Queue.pool.put(queue)
			}
			for (let event of handler)
				queue.add((queueComplete) => {
					event.callBack.call(event.caller, queueComplete, ...param)
				})

			queue.run()
			return queue
		}

		public hasEvent(eventName: string | number, callBack: Function, caller: any): boolean {
			let handler = this.eventMap.get(eventName)
			if (!handler)
				return false
			for (let event of handler) {
				if (event && event.callBack == callBack && event.caller == caller)
					return true
			}
			return false
		}

		public hasEventName(eventName: string | number) {
			let handler = this.eventMap.get(eventName)
			if (!handler)
				return false
			if (handler.length == 0)
				return false
			return true
		}

		public clearEvent() {
			this.eventMap.clear()
			this.emit(EventDispatcherEventType.CLEAR)
		}

	}
}